package net.luohuasheng.bee.proxy.jdbc.common.enums;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.sql.Driver;
import java.util.*;

/**
 * Token类型
 *
 * @author luohuasheng
 * @date 2016年4月1日 下午8:21:53
 */
public class DriverType {

    private static final Map<String, DriverType> DRIVERS = new HashMap<>();

    /**
     * 驱动程序包名：jconn2.jar 或jconn3.jar
     * <br>
     * 驱动程序类名: com.sybase.jdbc2.jdbc.SybDriver (com.sybase.jdbc3.jdbc.SybDriver)
     * <br>
     * JDBC URL: jdbc:sybase:Tds:[host]:[port] 或 jdbc:sybase:Tds:[host]:[port]?ServiceName=[database_name]
     * 默认端口5000
     */
    public static final DriverType SYBASE = new DriverType("Sybase", "jdbc:sybase:Tds:", "sybase", "com.sybase.jdbc2.jdbc.SybDriver", "com.sybase.jdbc2.jdbc.SybDriver");

    /**
     * 驱动程序包名：db2jcc.jar db2jcc_license_cu.jar
     * <br>
     * 驱动程序类名: com.ibm.db2.jcc.DB2Driver
     * <br>
     * JDBC URL: jdbc:db2://[host]:[port]/[database_name] 或 jdbc:db2:[database_name]
     */
    public static final DriverType DB2 = new DriverType("DB2", "jdbc:db2:", "db2", "com.ibm.db2.jcc.DB2Driver");

    /**
     * 驱动程序类名: com.sap.db.jdbc.Driver
     * <br>
     * JDBC URL: jdbc:sap://[host]:[port]?reconnect=true
     */
    public static final DriverType HANA = new DriverType("haha", "jdbc:sap:", "haha", "com.sap.db.jdbc.Driver");

    /**
     * 驱动程序包名：ifxjdbc.jar
     * <br>
     * 驱动程序类名: com.informix.jdbc.IfxDriver
     * <br>
     * JDBC URL: jdbc:informix-sqli://[host]:[port]/[dbname]:INFORMIXSERVER=[server-name]
     */
    public static final DriverType INFORMIX = new DriverType("Informix", "jdbc:informix-sqli:", "informix", "com.informix.jdbc.IfxDriver");

    /**
     * 驱动程序包名：ojdbc14.jar
     * <br>
     * 驱动程序类名: Oracle.jdbc.driver.OracleDriver
     * <br>
     * JDBC URL:* jdbc:oracle:thin:@//[host]:[port]/ServiceName* 或 jdbc:oracle:thin:@[host]:[port]:[SID] 或 jdbc:oracle:thin:@TNSName
     */
    public static final DriverType ORACLE = new DriverType("Oracle", "jdbc:oracle:thin:", "oracle", "Oracle.jdbc.driver.OracleDriver", "oracle.jdbc.driver.OracleDriver");

    /**
     * 驱动程序包名：msbase.jar mssqlserver.jar msutil.jar
     * <br>
     * 驱动程序类名: com.microsoft.jdbc.sqlserver.SQLServerDriver
     * <br>
     * JDBC URL: jdbc:microsoft:sqlserver://[server-name]:[port]
     * <br>
     * 默认端口1433，如果服务器使用默认端口则port可以省略
     */
    public static final DriverType SQLSERVER2000 = new DriverType("MS-SQL-2000", "jdbc:microsoft:sqlserver:", "sqlserver", "com.microsoft.jdbc.sqlserver.SQLServerDriver");

    /**
     * 驱动程序包名：sqljdbc.jar
     * <br>
     * 驱动程序类名: com.microsoft.sqlserver.jdbc.SQLServerDriver
     * <br>
     * JDBC URL: jdbc:sqlserver://[server-name]:[port]
     * <br>
     * 默认端口1433，如果服务器使用默认端口则port可以省略
     */
    public static final DriverType SQLSERVER = new DriverType("MS-SQL", "jdbc:sqlserver:", "sqlserver", "com.microsoft.sqlserver.jdbc.SQLServerDriver");

    /**
     * 驱动程序类名: org.apache.derby.jdbc.ClientDriver
     * <br>
     * JDBC URL: jdbc:derby://[host]:[port]/[dbname]
     */
    public static final DriverType DERBY = new DriverType("Derby", "jdbc:derby:", "derby", "org.apache.derby.jdbc.ClientDriver");


    /**
     * 驱动程序类名: org.hsqldb.jdbcDriver
     * <br>
     * JDBC URL: jdbc:hsqldb:filePath
     * <br>
     * 默认端口 无
     */
    public static final DriverType HSQLDB = new DriverType("HSQL", "jdbc:hsqldb:", "hsql", "org.hsqldb.jdbcDriver ");

    /**
     * 驱动程序类名: com.mariadb.jdbc.Driver
     * <br>
     * JDBC URL: jdbc:mariadb://[host]:[port]/[database_name]
     * <br>
     * 默认端口3306，如果服务器使用默认端口则port可以省略
     */
    public static final DriverType MARIADB = new DriverType("MariaDB", "jdbc:mariadb:", "mariadb", "org.mariadb.jdbc.Driver");

    /**
     * 驱动程序包名：MySQL-connector-Java-x.x.xx-bin.jar
     * 国产数据库实现 1、OceanBase[阿里/蚂蚁金服]
     * <br>
     * 驱动程序类名: com.mysql.jdbc.Driver
     * <br>
     * JDBC URL: jdbc:mysql://[host]:[port]/[database_name]
     * <br>
     * 默认端口3306，如果服务器使用默认端口则port可以省略
     */
    public final static DriverType MYSQL = new DriverType("MySQL", "jdbc:mysql:", "mysql", "com.mysql.cj.jdbc.Driver", "com.mysql.jdbc.Driver");

    /**
     * 驱动程序包名：驱动程序类名: org.postgresql.Driver
     * <br>
     * JDBC URL: jdbc:postgresql://[host]:[port]/[database_name]
     * <br>
     * 默认端口5432
     */
    public static final DriverType POSTGRES = new DriverType("POSTGRES", "jdbc:postgresql:", "postgresql", "org.postgresql.Driver");

    /**
     * 驱动程序包名：terajdbc4.jar tdgssjava.jar gui.jar
     * <br>
     * 驱动程序类名: com.ncr.teradata.TeraDriver
     * <br>
     * JDBC URL: jdbc:teradata://DatabaseServerName/Param1,Param2,... 或者 jdbc:teradata://GatewayServerName:PortNumber/DatabaseServerName/Param1,Param2,...
     */
    public static final DriverType TERADATA = new DriverType("Teradata", "jdbc:teradata:", "teradata", "com.ncr.teradata.TeraDriver");

    /**
     * 驱动程序包名：terajdbc4.jar tdgssjava.jar gui.jar
     * <br>
     * 驱动程序类名: org.netezza.Driver
     * <br>
     * JDBC URL: jdbc:netezza://[host]:[port]/[database_name]
     */

    public static final DriverType NETEZZA = new DriverType("Netezza", "jdbc:netezza:", "netezza", "org.netezza.Driver");

    //以下是嵌入式数据库

    /**
     * 驱动程序类名: org.h2.Driver
     * <br>
     * JDBC URL: jdbc:h2:filePath
     * <br>
     * 默认端口 无
     */
    public static final DriverType H2 = new DriverType("H2", "jdbc:h2:", "h2", "org.h2.Driver");

    /**
     * 驱动程序包名：sqlitejdbc-v056.jar
     * <br>
     * 驱动程序类名: org.sqlite.JDBC
     * <br>
     * JDBC URL: jdbc:sqlite:filePath
     * <br>
     * 默认端口 无
     */
    public static final DriverType SQLITE = new DriverType("sqlite", "jdbc:sqlite:", "sqlite", "org.sqlite.JDBC");

    // 以下国产数据库

    /**
     * POLARDB[阿里云]
     * <br>
     * 驱动程序类名: com.aliyun.polardb.Driver
     * <br>
     * JDBC URL: jdbc:polardb://[host]:[port]/[dbname]
     */
    public static final DriverType POLARDB = new DriverType("PolarDB", "jdbc:polardb:", "postgresql", "com.aliyun.polardb.Driver");

    /**
     * 达梦数据库
     * <br>
     * 驱动程序类名: dm.jdbc.driver.DmDriver
     * <br>
     * JDBC URL: jdbc:dm://[host]:[port]/[database_name]
     */

    public static final DriverType DM = new DriverType("dm", "jdbc:dm:", "dm", "dm.jdbc.driver.DmDriver");


    /**
     * <br>
     * GaussDB(高斯数据库)[华为技术有限公司]
     * <br>
     * CynosDB[腾讯云计算（北京）有限责任公司]
     * <br>
     * DM(达梦数据库)[武汉达梦数据库有限公司]
     * <br>
     * GBase[天津南大通用数据技术股份有限公司]
     * <br>
     * Kingbase ES[北京人大金仓信息技术股份有限公司]
     * <br>
     * OSCAR(神通数据库)[天津神舟通用数据技术有限公司]
     * <br>
     * Obase[上海丛云信息科技有限公司]
     * <br>
     * EsgynDB(易鲸捷数据库)[贵州易鲸捷信息技术有限公司]
     * <br>
     * TiDB[北京平凯星辰科技发展有限公司]
     * <br>
     * SequoiaDB(巨杉数据库)[广州巨杉软件开发有限公司]
     * <br>
     * K-DB[浪潮集团有限公司]
     * <br>
     * OpenBASE[东软集团有限公司]
     * <br>
     * Huayisoft DB Server[九江易龙技术有限公司]
     * <br>
     * HighGo DB(瀚高数据库)[瀚高基础软件股份有限公司]
     * <br>
     * SinoDB(星瑞格数据库)[福建星瑞格软件有限公司]
     * <br>
     * HUABASE[清华-广东环天数据与知识工程研究中心]
     * <br>
     * iBASE[北京国信贝斯软件有限公司]
     * <br>
     * UXDB(优炫数据库)[北京优炫软件股份有限公司]
     */

    private String jdbcUrl;
    private String dbType;
    private final String code;
    private String[] driversClass;
    private static final Map<String, Driver> DRIVER_MAP = new HashMap<>();

    static {
        for (Field field : DriverType.class.getFields()) {
            if (field.getType().equals(DriverType.class)) {
                try {
                    DriverType driverType = (DriverType) field.get(DriverType.class);
                    DRIVERS.put(driverType.code, driverType);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public String getCode() {
        return code;
    }


    public String[] getDriversClass() {
        return driversClass;
    }

    public String getJdbcUrl() {
        return jdbcUrl;
    }

    DriverType(String code, String jdbcUrl, String dbType, String... driversClass) {
        this.code = code;
        this.dbType = dbType;
        this.driversClass = driversClass;
        this.jdbcUrl = jdbcUrl;
    }

    public static DriverType getType(String code, String jdbcUrl, String dbType, String... driversClass) {
        DriverType driverType = DRIVERS.get(code);
        if (driverType == null) {
            driverType = new DriverType(code, jdbcUrl, dbType, driversClass);
        } else {
            driverType.dbType = dbType;
            driverType.driversClass = driversClass;
            driverType.jdbcUrl = jdbcUrl;
        }
        return driverType;
    }

    public static DriverType getTypeByCode(String code) {
        for (DriverType driverType : DriverType.values()) {
            if (driverType.getCode().equals(code)) {
                return driverType;
            }
        }
        return DriverType.MYSQL;
    }


    public static DriverType getDriverTypeForStartUrl(String url) {

        for (DriverType driverType : DriverType.values()) {
            if (url.startsWith(driverType.jdbcUrl)) {
                return driverType;
            }
        }
        return DriverType.MYSQL;

    }

    public static Driver getDriverForStartUrl(String url) {
        Driver driver = DRIVER_MAP.get(url);
        if (driver == null) {
            for (DriverType element : DriverType.values()) {
                if (url.startsWith(element.jdbcUrl)) {
                    return loadDriver(element.driversClass);
                }
            }
            driver = loadDriver(MYSQL.driversClass);
            DRIVER_MAP.put(url, driver);
        }
        return driver;
    }

    private static Collection<DriverType> values() {
        return DRIVERS.values();
    }


    private static Driver loadDriver(String[] driversClass) {
        for (String driverClass : driversClass) {
            try {
                Class<?> classZ = DriverType.class.getClassLoader().loadClass(driverClass);
                return (Driver) classZ.getDeclaredConstructor().newInstance();
            } catch (ClassNotFoundException | IllegalAccessException | InstantiationException | NoSuchMethodException | InvocationTargetException e) {
                e.printStackTrace();
            }

        }
        return null;
    }


    public String getDbType() {
        return dbType;
    }


    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        DriverType that = (DriverType) o;
        return code.equals(that.code);
    }

    @Override
    public int hashCode() {
        return Objects.hash(code);
    }
}